﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Xml.Linq;

// using Lcs.TraceVisualiser.Forms.Components;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.Forms;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.BlockingVisualiser.Forms;
using System.Windows.Forms;

namespace Noris.Tools.TraceVisualiser.Green.SqlServer
{
	#region EventItem : data jedné události v trace souboru blokování
	/// <summary>
    /// EventItem : data jedné události v trace souboru blokování
    /// </summary>
    internal class EventItem : IEventItem, TraceVisualiser.Forms.IMasterDetails, ISqlEventData
    {
        #region Vytvoření instance - tato třída se vytváří přímo z textu řádku, bez přechodu přes objekt TraceItem
		/// <summary>
		/// Vytvoří a vrátí objekt EventItem, anebo null pokud není korektní zadání
		/// </summary>
		/// <param name="stringHeap"></param>
		/// <param name="traceInfo">Soubor Trace</param>
		/// <param name="lineNumber">Číslo řádky</param>
		/// <param name="lineText">Text řádky</param>
		/// <param name="persistData">Trvalá data ze serveru</param>
		/// <returns></returns>
		internal static EventItem CreateItem(StringHeap stringHeap, TraceInfo traceInfo, int lineNumber, XElement xEvent, ref TraceServerPersistData persistData)
        {
			if (stringHeap == null || traceInfo == null || xEvent == null)
				return null;

            EventItem info = new EventItem(stringHeap);
			info.ReadXmlData(traceInfo, lineNumber, xEvent, ref persistData);

			return info;
        }
        private EventItem(StringHeap stringHeap)
        {
            this.StringHeap = stringHeap;
            this.Time = TimeRange.Empty;
            this.Properties = ElementProperties.Selectable;
            this.ActionOnDoubleClick = Components.ElementActionType.DefaultByRow;
			this.LineNumber = 0;
            this.BackColor = Color.Empty;
        }
        protected StringHeap StringHeap;
        #endregion
	    #region Plnění objektu EventItem z dat vstupního Xml elementu Event
		/// <summary>
		/// Do objektu this vepíše data z příchozí události (prvky elementu Event z trace)
		/// </summary>
		/// <param name="traceInfo">Soubor Trace</param>
		/// <param name="lineNumber">Číslo řádky</param>
		/// <param name="xEvent">Data eventu</param>
		/// <param name="persistData">Trvalá data ze serveru</param>
		protected virtual void ReadXmlData(TraceInfo traceInfo, int lineNumber, XElement xEvent, ref TraceServerPersistData persistData)
        {
            this.EventPart = EventPartType.None;
			if (xEvent == null) return;

			// 1. V elementu xEvent bývají atributy "id" a "name", které specifikují druh události:
            string eventId = GetAttributeString(xEvent, "id");
			this.SqlEventId = eventId;
			this.SqlEventName = GetAttributeString(xEvent, "name");
			this.SqlServerName = persistData.ServerInformation;

            // Předem: máme některé eventy, které nebudeme vůbec ukládat - protože je jich moc, a primárně nejsou zajímavé.
            // PS: tady by se hodil filtr v deklaraci dokumentu, hodně podobný jako filtr na události aplikačního serveru !!!
            if (!persistData.IsEventAllowed(eventId, this.SqlEventName, traceInfo.Document))
                return;

			// id="59" name="Lock:Deadlock Chain">
			// id="64510" name="Profiler Message Event"

			foreach (XElement xColumn in xEvent.Elements())
			{
				if (xColumn.Name.LocalName == "Column")
				{
                    // Fyzická data z XML formátu:
					string id = GetAttributeString(xColumn, "id");
					string name = GetAttributeString(xColumn, "name");
					string value = xColumn.Value;

					// Některé položky známe a uložíme jejich hodnoty do vyhrazených property:
					switch (id)
					{
						case SQLTRACE_COLUMN_TextData:
							this.TextData = value;
							break;
						case SQLTRACE_COLUMN_BinaryData:
							// zahodíme?
							break;
						case SQLTRACE_COLUMN_ClientProcessID:
							this.ClientProcessID = value;
							break;
						case SQLTRACE_COLUMN_ApplicationName:
							this.ApplicationName = value;
							break;
						case SQLTRACE_COLUMN_LoginName:
							this.LoginName = value;
							break;
						case SQLTRACE_COLUMN_SPID:
							this.SpID = GetInt32(value);
							break;
						case SQLTRACE_COLUMN_Duration:
							this.Duration = GetInt32(value);
							break;
						case SQLTRACE_COLUMN_StartTime:
							this.StartTime = GetDateTimeN(value);
							break;
						case SQLTRACE_COLUMN_EndTime:
							this.EndTime = GetDateTimeN(value);
							break;
						case SQLTRACE_COLUMN_Reads:
							this.Reads = GetInt32(value);
							break;
						case SQLTRACE_COLUMN_Writes:
							this.Writes = GetInt32(value);
							break;
						case SQLTRACE_COLUMN_CPU:
							this.CPU = GetInt32(value);
							break;
						default:
                            // Položky, které neznáme, uložíme do obecného seznamu:
                            this.Columns.Add(new EventItemColumn(this, id, name, value));
							break;
					}
				}
			}

            // Nyní podle druhu události zajistím její zpracování:
			this.TargetPathList = new List<string>();
			this.TargetPathRoot = SqlServerConstants.RootNodeSqlServer + "\\" + this.SqlServerName;
			this.HideZeroWidthElementRectangle = false;
			this.ImagePivotElementPoint = ContentAlignment.MiddleCenter;
			this.ImagePivotImagePoint = ContentAlignment.MiddleCenter;
            switch (eventId)
            {
                case SQLTRACE_EVENT_Rpc:                  //    10: RPC:Completed          obsahuje kompletní event
                    this.FinalizeEventRpc();
                    break;
                case SQLTRACE_EVENT_SqlBatchEnd:          //    12: SQL:BatchCompleted     obsahuje konec SQL příkazu (exec, select, update, ...)
                    this.FinalizeEventSqlBatchEnd();
                    break;
                case SQLTRACE_EVENT_SqlBatchStarting:     //    13: SQL:BatchStarting      obsahuje začátek SQL příkazu (exec, select, update, ...)
                    this.FinalizeEventSqlBatchStart();
                    break;
                case SQLTRACE_EVENT_AuditLogin:           //    14: Audit Login            obsahuje StartTime;  TextData (settings);  SPID;  ...
                    this.FinalizeEventAuditLogin();
                    break;
                case SQLTRACE_EVENT_AuditLogout:          //    15: Audit Logout           obsahuje EndTime;  SPID; ....
                    this.FinalizeEventAuditLogout();
                    break;
                case SQLTRACE_EVENT_ExistingConnection:   //    17: ExistingConnection     obsahuje TextData (settings);  SPID;  LoginName;  ApplicationName;  StartTime
                    this.FinalizeEventExistConnect();
                    break;
                case SQLTRACE_EVENT_LockDeadlock:         //    25: Lock:Deadlock          obsahuje Start;  End;  Duration;  SPID;  ClientProcessID;  LoginName;  ApplicationName
                    this.FinalizeEventLockDeadlock();
                    break;
                case SQLTRACE_EVENT_SpStmtCompleted:      //    45: SP:StmtCompleted       obsahuje jeden izolovaný příkaz ze StoredProcedury, po jeho dokončení. Tyto položky se mergují.
                    this.FinalizeEventSpStmtCompleted();
                    break;
                case SQLTRACE_EVENT_LockDeadlockChain:    //    59: Lock:Deadlock Chain    obsahuje TextData: Deadlock Chain SPID = 851:980012;   SPID;  StartTime
                    this.FinalizeEventLockDeadlockChain();
                    break;
                case SQLTRACE_EVENT_DeadlockGraph:        //   148: Deadlock graph         obsahuje TextData=XML data grafu deadlocku;  StartTime;  LoginName;  SPID
                    this.FinalizeEventDeadlockGraph();
                    break;
                case SQLTRACE_EVENT_ProfilerMessage:      // 64510: Profiler Message Event obsahuje id=1 TextData: This is file 19 of a rollover file. Previous file is C:\asol\DeadLocks_220_18.trc.
                    this.FinalizeEventMessage();
                    break;
                case SQLTRACE_EVENT_TraceStop:            // 65533: Trace Stop             obsahuje pouze StartTime (nikoli EndTime)
                    this.FinalizeEventTraceStop();
                    break;
                case SQLTRACE_EVENT_TraceStart:           // 65534: Trace Start            obsahuje StartTime
                    this.FinalizeEventTraceStart();
                    break;
                default:
                    this.FinalizeEventDefault();
                    break;
            }

			// Cíl elementu:
			this.TargetHomeNode = null;
			this.TargetPaths = this.TargetPathList.ToArray();
			this.TargetPathList = null;
			this.TreePaths = null;
			this.Trace = traceInfo;

			// Dokončení:
            this.LineNumber = lineNumber;
        }
		protected static string GetAttributeString(XElement xElement, string attributeName)
		{
			XAttribute att = xElement.Attribute(attributeName);
			if (att == null) return "";
			if (att.Value == null) return "";
			return att.Value;
		}
		protected static int GetAttributeInt32(XElement xElement, string attributeName)
		{
			XAttribute att = xElement.Attribute(attributeName);
			if (att == null) return 0;
			if (att.Value == null) return 0;
			return GetInt32(att.Value);
		}
		protected static int GetInt32(string text)
		{
			if (String.IsNullOrEmpty(text)) return 0;
			int value;
			if (!Int32.TryParse(text, out value)) return 0;
			return value;
		}
		protected static DateTime? GetDateTimeN(string text)
		{
			if (String.IsNullOrEmpty(text)) return null;
			return TraceReader.ParseUtcDateTime(text);
		}
		#endregion
        #region Konstanty definující ID eventů a ID sloupců z SQL trace

        internal const string SQLTRACE_COLUMN_TextData = "1";
        internal const string SQLTRACE_COLUMN_BinaryData = "2";
        internal const string SQLTRACE_COLUMN_ClientProcessID = "3";
        internal const string SQLTRACE_COLUMN_ApplicationName = "10";
        internal const string SQLTRACE_COLUMN_LoginName = "11";
        internal const string SQLTRACE_COLUMN_SPID = "12";
        internal const string SQLTRACE_COLUMN_Duration = "13";
        internal const string SQLTRACE_COLUMN_StartTime = "14";
        internal const string SQLTRACE_COLUMN_EndTime = "15";
        internal const string SQLTRACE_COLUMN_Reads = "16";
        internal const string SQLTRACE_COLUMN_Writes = "17";
        internal const string SQLTRACE_COLUMN_CPU = "18";

        internal const string SQLTRACE_EVENT_Rpc = "10";
        internal const string SQLTRACE_EVENT_SqlBatchEnd = "12";
        internal const string SQLTRACE_EVENT_SqlBatchStarting = "13";
        internal const string SQLTRACE_EVENT_AuditLogin = "14";
        internal const string SQLTRACE_EVENT_AuditLogout = "15";
        internal const string SQLTRACE_EVENT_ExistingConnection = "17";
        internal const string SQLTRACE_EVENT_LockDeadlock = "25";
        internal const string SQLTRACE_EVENT_SpStmtCompleted = "45";
        internal const string SQLTRACE_EVENT_LockDeadlockChain = "59";
        internal const string SQLTRACE_EVENT_DeadlockGraph = "148";
        internal const string SQLTRACE_EVENT_ProfilerMessage = "64510";
        internal const string SQLTRACE_EVENT_TraceStop = "65533";
        internal const string SQLTRACE_EVENT_TraceStart = "65534";
        #endregion
        #region Zpracování jednotlivých typů eventů poté, kdy byla načtena základní data z Column do zdejších Property
        protected void FinalizeEventRpc()
        {
/*
    <Event id="10" name="RPC:Completed">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="2" name="BinaryData">00000000000000002600730070005F00720065007300650074005F0063006F006E006E0065006300740069006F006E00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="16" name="Reads">0</Column>
      <Column id="18" name="CPU">0</Column>
      <Column id="1" name="TextData">exec sp_reset_connection </Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">78</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>
*/
            this.EventPart = EventPartType.Complete;
            this.Time = GetDurationTimeRange();            // Čas uvedený v tomto Eventu (Start+ End) není časem výkonu SQL, ale časem zápisu. Čas výkonu je dán Duration [ms] až EndTime.
			this.FinalizeAddTargetPathNodeSpid();
			this.ContainSqlCommand = true;
			this.ColorGroup = this.SqlCommandColorGroup;
		}
        protected void FinalizeEventSqlBatchEnd()
        {
/*
    <Event id="12" name="SQL:BatchCompleted">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="16" name="Reads">8</Column>
      <Column id="18" name="CPU">0</Column>
      <Column id="1" name="TextData">exec lcs.f_connect_dotnet 'CZ','','','1087400072773999958',3699842 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SET ANSI_NULLS ON SET DATEFORMAT ymd</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">1199</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>
*/
            this.EventPart = EventPartType.Complete;
            this.Time = GetDurationTimeRange();            // Čas uvedený v tomto Eventu (Start+ End) není časem výkonu SQL, ale časem zápisu. Čas výkonu je dán Duration [ms] až EndTime.
			this.FinalizeAddTargetPathNodeSpid();
			this.ContainSqlCommand = true;
            this.ColorGroup = this.SqlCommandColorGroup;
        }
        protected void FinalizeEventSqlBatchStart()
        {
            this.EventPart = EventPartType.Start;
			this.FinalizeAddTargetPathNodeSpid();
		}
        protected void FinalizeEventAuditLogin()
        {
/*
    <Event id="14" name="Audit Login">
      <Column id="1" name="TextData">-- network protocol: TCP/IP
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read uncommitted
</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
    </Event>
*/
            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
			this.FinalizeAddTargetPathNodeSpid();
			this.ElementImage = Noris.Tools.TraceVisualiser.Components.PicLibrary16.Identity_Cached;
            this.ColorGroup = SqlServerConstants.ColorLogin;
        }
        protected void FinalizeEventAuditLogout()
        {
/*
    <Event id="15" name="Audit Logout">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:14.967+01:00</Column>
      <Column id="16" name="Reads">319299</Column>
      <Column id="18" name="CPU">625</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">12540000</Column>
      <Column id="17" name="Writes">545</Column>
    </Event>
 */
            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
			this.FinalizeAddTargetPathNodeSpid();
			this.ColorGroup = SqlServerConstants.ColorLogout;
        }
        protected void FinalizeEventExistConnect()
        {
/*
    <Event id="17" name="ExistingConnection">
      <Column id="1" name="TextData">-- network protocol: TCP/IP
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat ymd
set datefirst 7
set transaction isolation level read uncommitted
</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:14.967+01:00</Column>
    </Event>
*/
            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
			this.FinalizeAddTargetPathNodeSpid();
			this.ElementImage = Noris.Tools.TraceVisualiser.Pics.PicLibrary16.ArrowRightDouble_Cached;
            this.ColorGroup = SqlServerConstants.ColorExistConnect;
        }
        protected void FinalizeEventLockDeadlock()
        {
/*
    <Event id="25" name="Lock:Deadlock">
      <Column id="1" name="TextData">1:980012</Column>
      <Column id="9" name="ClientProcessID">6020</Column>
      <Column id="11" name="LoginName">netuser</Column>
      <Column id="13" name="Duration">3058000</Column>
      <Column id="15" name="EndTime">2011-11-08T12:50:01.67+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green ()</Column>
      <Column id="12" name="SPID">85</Column>
      <Column id="14" name="StartTime">2011-11-08T12:49:58.613+01:00</Column>
    </Event>
*/
            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
			this.FinalizeAddTargetPathNodeSpid();
			this.ImagePivotElementPoint = ContentAlignment.MiddleRight;
			this.ImagePivotImagePoint = ContentAlignment.MiddleCenter;
			this.ColorGroup = SqlServerConstants.ColorLockDeadlock;
			this.ElementImage = TraceVisualiser.Pics.PicLibrary16.LockRed3_Cached;
        }
        protected void FinalizeEventSpStmtCompleted()
        {
            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
            this.FinalizeAddTargetPathNodeSpid();
            // Tento event se možná bude mergovat se svými předchůdci / následovníky, a to tehdy, pokud bude ve shodném čase (milisekunda !) a bude mít shodnou hodnotu
            //   Mergování: jádro TraceData člení události do jejich procesů (procesy zde = SQL server + SPID), a v nich ukládá do oddělených soupisů ty prvky, 
            //   které deklarují neprázdný klíč IEventItem.MergeKey. Do jednoho soupisu k mergování spadají pouze prvky, které mají shodný IEventItem.MergeKey.
            this.MergeKey = "SpStmt:" + this.ClientProcessID;         // Sloučím pouze prvky typu "SpStmt" ze shodného procesu.
            this.ColorGroup = SqlServerConstants.ColorStoreProcedure;
        }
        protected void FinalizeEventLockDeadlockChain()
        {
/*
    <Event id="59" name="Lock:Deadlock Chain">
      <Column id="1" name="TextData">Deadlock Chain SPID = 152
1:1493845             
</Column>
      <Column id="12" name="SPID">9</Column>
      <Column id="14" name="StartTime">2011-11-08T12:50:01.67+01:00</Column>
    </Event>
*/
            this.EventPart = EventPartType.None;
        }
        protected void FinalizeEventDeadlockGraph()
        {
            /*
    <Event id="148" name="Deadlock graph">
      <Column id="1" name="TextData">&lt;deadlock-list&gt; ... (textový XML kód grafu) ... </Column>
      <Column id="11" name="LoginName">sa</Column>
      <Column id="12" name="SPID">25</Column>
      <Column id="14" name="StartTime">2011-11-08T12:50:01.677+01:00</Column>
    </Event>
            */

            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
			this.FinalizeAddTargetPathNodeRoot();
			this.FinalizeAddTargetPathNodeSpid();
			this.HideZeroWidthElementRectangle = true;
            this.ElementImage = TraceVisualiser.Pics.PicLibrary16.DeadlockGraph_Cached;
            this.ActionOnDoubleClick = Components.ElementActionType.ExecHandler;        // DoubleClick na elementu zajistí vyvolání zdejšího handleru OnDoubleClick (protože jej vrací zdejší property IDataElement.DoubleClick)
        }
        protected void FinalizeEventMessage()
        {
            
        }
        protected void FinalizeEventTraceStop()
        {
/*
    <Event id="65533" name="Trace Stop">
        <Column id="14" name="StartTime">2012-02-08T16:04:39.36+01:00</Column>
    </Event>
*/
            this.EventPart = EventPartType.None;
        }
        protected void FinalizeEventTraceStart()
        {
/*
    <Event id="65534" name="Trace Start">
        <Column id="14" name="StartTime">2012-02-08T16:04:19.277+01:00</Column>
    </Event>
*/
            this.EventPart = EventPartType.None;
        }
        protected void FinalizeEventIgnored()
        {
            this.EventPart = EventPartType.None;
        }
        protected void FinalizeEventDefault()
        {
            this.EventPart = EventPartType.Complete;
            this.Time = GetCurrentTimeRange();
			this.FinalizeAddTargetPathNodeSpid();
			this.ColorGroup = SqlServerConstants.ColorOther;
        }
		/// <summary>
		/// Do seznamu řádků, v nichž se tento event objeví, přidá root = Sql server / jméno serveru
		/// </summary>
		protected void FinalizeAddTargetPathNodeRoot()
		{
			this.TargetPathList.Add(this.TargetPathRoot);
		}
		/// <summary>
		/// Do seznamu řádků, v nichž se tento event objeví, přidá SPID = Sql server / jméno serveru / SPID
		/// </summary>
		protected void FinalizeAddTargetPathNodeSpid()
		{
            if (this.SpID > 0)
                this.TargetPathList.Add(this.TargetPathRoot + "\\" + GetSpidAsNodeKey());
		}
        /// <summary>
        ///  Vrátí časový úsek daný StartTime ÷ EndTime, 
        ///  anebo daný tím časem, který je zadaný (anebo Empty).
        /// </summary>
        /// <returns></returns>
        protected TimeRange GetCurrentTimeRange()
        {
            if (this.ContainValidStartEndTime)
                return new TimeRange(this.StartTime.Value, this.EndTime.Value);
            else if (this.EndTime.HasValue && this.Duration > 0)
                return GetDurationTimeRange();
            else if (this.StartTime.HasValue)
                return new TimeRange(this.StartTime.Value, this.StartTime.Value);
            else if (this.EndTime.HasValue)
                return new TimeRange(this.EndTime.Value, this.EndTime.Value);
            return TimeRange.Empty;
        }
        /// <summary>
        /// Vrátí časový úsek daný koncem (EndTime) a dobou trvání (Duration) v milisekundách.
        /// </summary>
        /// <returns></returns>
        protected TimeRange GetDurationTimeRange()
        {
            if (this.EndTime.HasValue)
                return new TimeRange(this.EndTime.Value.AddMilliseconds((double)(-this.Duration) / 1000D), this.EndTime.Value);
            return TimeRange.Empty;
        }
        /// <summary>
        /// Vrací true, pokud this obsahuje reálné hodnoty StartTime a EndTime (z nichž se má vytvořit TimeRange tohoto eventu).
        /// Vrací false, když:
        /// a) některá z hodnot je null
        /// b) obě hodnoty jsou shodné a přitom Duration je kladné a větší než 400 (mikrosekund) => pak se má čas určit pomocí metody GetDurationTimeRange()
        /// </summary>
        protected bool ContainValidStartEndTime
        {
            get
            {
                if (!this.StartTime.HasValue || !this.EndTime.HasValue) return false;
                if (this.StartTime.Value == this.EndTime.Value && this.Duration > 400) return false;
                return true;
            }
        }
        /// <summary>
        /// Vrací SPID ve formě "0123", anebo pokud je SPID 0 nebo záporné, pak vrací "".
        /// </summary>
        /// <returns></returns>
        protected string GetSpidAsNodeKey()
        {
            if (this.SpID > 0)
                return this.SpID.ToString("0000");
            return "";
        }
        #endregion
        #region Mergování eventů
        /// <summary>
        /// 
        /// </summary>
        /// <param name="nextEventItem"></param>
        /// <returns></returns>
        protected bool MergeWith(EventItem nextEventItem)
        {
            if (nextEventItem == null) return false;
            if (nextEventItem.Time.Begin > this.Time.Begin) return false;       // Pokud daný příkaz začíná v pozdějším čase než začínáme my, pak mergovat nebudeme. Prvek next zobrazíme jako oddělený další prvek.

            // Merguji nextEvent do this:
            this.Time = this.Time + nextEventItem.Time;
            this.TextData = this.TextData + Environment.NewLine + nextEventItem.TextData;
            this.Duration += nextEventItem.Duration;
            this.Reads += nextEventItem.Reads;
            this.Writes += nextEventItem.Writes;
            this.CPU += nextEventItem.CPU;

            return true;
        }
        #endregion
        #region Vzorky eventů z Trace souboru
        /*
    <Event id="64510" name="Profiler Message Event">
      <Column id="1" name="TextData">This is file 19 of a rollover file. Previous file is C:\asol\DeadLocks_220_18.trc.</Column>
    </Event>

    <Event id="59" name="Lock:Deadlock Chain">
      <Column id="1" name="TextData">Deadlock Chain SPID = 851:980012</Column>
      <Column id="12" name="SPID">9</Column>
      <Column id="14" name="StartTime">2011-11-08T12:50:01.667+01:00</Column>
    </Event>

    <Event id="25" name="Lock:Deadlock">
      <Column id="1" name="TextData">1:980012</Column>
      <Column id="9" name="ClientProcessID">6020</Column>
      <Column id="11" name="LoginName">netuser</Column>
      <Column id="13" name="Duration">3058000</Column>
      <Column id="15" name="EndTime">2011-11-08T12:50:01.67+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green ()</Column>
      <Column id="12" name="SPID">85</Column>
      <Column id="14" name="StartTime">2011-11-08T12:49:58.613+01:00</Column>
    </Event>

    <Event id="148" name="Deadlock graph">
      <Column id="1" name="TextData">&lt;deadlock-list&gt;      ... XML popis dat grafu deadlocku, to bude rozebráno v jiném objektu ... </Column>
      <Column id="11" name="LoginName">sa</Column>
      <Column id="12" name="SPID">25</Column>
      <Column id="14" name="StartTime">2011-11-08T12:50:01.677+01:00</Column>
    </Event>

    <Event id="65534" name="Trace Start">
      <Column id="14" name="StartTime">2012-02-08T16:04:19.277+01:00</Column>
    </Event>

    <Event id="17" name="ExistingConnection">
      <Column id="1" name="TextData">-- network protocol: TCP/IP
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat ymd
set datefirst 7
set transaction isolation level read uncommitted
</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:14.967+01:00</Column>
    </Event>

    <Event id="15" name="Audit Logout">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:14.967+01:00</Column>
      <Column id="16" name="Reads">319299</Column>
      <Column id="18" name="CPU">625</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">12540000</Column>
      <Column id="17" name="Writes">545</Column>
    </Event>

    <Event id="10" name="RPC:Completed">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="2" name="BinaryData">00000000000000002600730070005F00720065007300650074005F0063006F006E006E0065006300740069006F006E00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="16" name="Reads">0</Column>
      <Column id="18" name="CPU">0</Column>
      <Column id="1" name="TextData">exec sp_reset_connection </Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">78</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>

    <Event id="14" name="Audit Login">
      <Column id="1" name="TextData">-- network protocol: TCP/IP
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read uncommitted
</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
    </Event>

    <Event id="13" name="SQL:BatchStarting">
      <Column id="1" name="TextData">exec lcs.f_connect_dotnet 'CZ','','','1087400072773999958',3699842 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SET ANSI_NULLS ON SET DATEFORMAT ymd</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
    </Event>

    <Event id="12" name="SQL:BatchCompleted">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.507+01:00</Column>
      <Column id="16" name="Reads">8</Column>
      <Column id="18" name="CPU">0</Column>
      <Column id="1" name="TextData">exec lcs.f_connect_dotnet 'CZ','','','1087400072773999958',3699842 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SET ANSI_NULLS ON SET DATEFORMAT ymd</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">1199</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>

    <Event id="13" name="SQL:BatchStarting">
      <Column id="1" name="TextData">SELECT cislo_poradace FROM lcs.subjekty WHERE cislo_subjektu = 14802</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.51+01:00</Column>
    </Event>

    <Event id="12" name="SQL:BatchCompleted">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.51+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">92</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.51+01:00</Column>
      <Column id="16" name="Reads">5</Column>
      <Column id="18" name="CPU">0</Column>
      <Column id="1" name="TextData">SELECT cislo_poradace FROM lcs.subjekty WHERE cislo_subjektu = 14802</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">322</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>

    <Event id="15" name="Audit Logout">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.51+01:00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">94</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:08.94+01:00</Column>
      <Column id="16" name="Reads">8711</Column>
      <Column id="18" name="CPU">172</Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">18570000</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>

    <Event id="10" name="RPC:Completed">
      <Column id="11" name="LoginName">netadmin</Column>
      <Column id="15" name="EndTime">2012-02-08T16:04:27.51+01:00</Column>
      <Column id="2" name="BinaryData">00000000000000002600730070005F00720065007300650074005F0063006F006E006E0065006300740069006F006E00</Column>
      <Column id="10" name="ApplicationName">Helios Green (vapp)</Column>
      <Column id="12" name="SPID">94</Column>
      <Column id="14" name="StartTime">2012-02-08T16:04:27.51+01:00</Column>
      <Column id="16" name="Reads">0</Column>
      <Column id="18" name="CPU">0</Column>
      <Column id="1" name="TextData">exec sp_reset_connection </Column>
      <Column id="9" name="ClientProcessID">7608</Column>
      <Column id="13" name="Duration">78</Column>
      <Column id="17" name="Writes">0</Column>
    </Event>

		*/
		#endregion
		#region Public datové property
        internal EventPartType EventPart { get; private set; }
		internal string SqlEventId { get { return this.StringHeap.GetString(this.SqlEventIdInt32); } private set { this.SqlEventIdInt32 = this.StringHeap.GetInt32(value); } }
		internal Int32 SqlEventIdInt32 { get; private set; }
		internal string SqlEventName { get { return this.StringHeap.GetString(this.SqlEventNameInt32); } private set { this.SqlEventNameInt32 = this.StringHeap.GetInt32(value); } }
		internal Int32 SqlEventNameInt32 { get; private set; }
		internal string SqlServerName { get { return this.StringHeap.GetString(this.SqlServerNameInt32); } private set { this.SqlServerNameInt32 = this.StringHeap.GetInt32(value); } }
		internal Int32 SqlServerNameInt32 { get; private set; }
        internal string MergeKey { get { return this.StringHeap.GetString(this.MergeKeyInt32); } private set { this.MergeKeyInt32 = this.StringHeap.GetInt32(value); } }
        internal Int32 MergeKeyInt32 { get; private set; }
		internal string TextData { get; private set; }         // Tuto property úmyslně neukládám do StringHeap, protože v 98% řádků bude jiná.
		internal string ClientProcessID { get { return this.StringHeap.GetString(this.ClientProcessIDInt32); } private set { this.ClientProcessIDInt32 = this.StringHeap.GetInt32(value); } }
		internal Int32 ClientProcessIDInt32 { get; private set; }
		internal string ApplicationName { get { return this.StringHeap.GetString(this.ApplicationNameInt32); } private set { this.ApplicationNameInt32 = this.StringHeap.GetInt32(value); } }
		internal Int32 ApplicationNameInt32 { get; private set; }
		internal string LoginName { get { return this.StringHeap.GetString(this.LoginNameInt32); } private set { this.LoginNameInt32 = this.StringHeap.GetInt32(value); } }
		internal Int32 LoginNameInt32 { get; private set; }
		internal int SpID { get; private set; }
		internal int Duration { get; private set; }
		internal DateTime? StartTime { get; private set; }
		internal DateTime? EndTime { get; private set; }
		internal int Reads { get; private set; }
		internal int Writes { get; private set; }
		internal int CPU { get; private set; }
        /// <summary>
        /// true = obsahuje SQL příkaz (pokud pochází z události typu RPC:Completed anebo SQL:BatchCompleted)
        /// </summary>
		internal bool ContainSqlCommand { get; private set; }

		internal string SqlCommandColorGroup
		{
			get
			{
				string sqlKeyword = this.SqlKeyword;
				if (String.IsNullOrEmpty(sqlKeyword))
					return SqlServerConstants.ColorCommand;
				return SqlServerConstants.ColorRoot + "." + sqlKeyword.ToLower();
			}
		}
        /// <summary>
        /// Obsahuje (vrací) první slovo z textu TextData.
        /// Používá se typicky pro event SqlBatch, kdy vrací slovo "update", "select", "insert", atd.
        /// </summary>
        internal string SqlKeyword
        {
            get
            {
				return this.FindSqlCommandInTextData(true);
            }
        }

        internal List<EventItemColumn> Columns
        {
            get
            {
                if (_Columns == null)
                    _Columns = new List<EventItemColumn>();
                return _Columns;
            }
        }
        private List<EventItemColumn> _Columns = null;

		// Data přímo z textu
		internal TimeRange Time { get; private set; }
		internal int Id { get { return this.LineNumber; } }
		// Data pro zobrazování
		public int LineNumber { get; protected set; }
		public string TargetHomeNode { get; protected set; }
		private List<string> TargetPathList { get; set; }
		private string TargetPathRoot;
		public string[] TargetPaths { get; protected set; }
		public TreePath[] TreePaths { get; protected set; }
		public string ColorGroup { get; protected set; }
		public int ColorIndex { get; protected set; }
		public int ElementGroup { get; protected set; }
		public bool HideZeroWidthElementRectangle { get; protected set; }
		public GraphElementShapeType ElementShapeType { get; protected set; }
		public ElementProperties Properties { get; protected set; }
        public ElementActionType ActionOnDoubleClick { get; protected set; }
		public Color BackColor { get; protected set; }
		public Color FontColor { get; protected set; }
		public ContentAlignment ImagePivotElementPoint { get; protected set; }
		public ContentAlignment ImagePivotImagePoint { get; protected set; }
		public Image ElementImage { get; protected set; }
		public TraceInfo Trace { get; protected set; }

		/// <summary>
		/// Klíč procesu = SQL Server (nikoli SPID)
		/// </summary>
		public string ProcessKey { get { return this.SqlServerName + "." + this.SpID.ToString(); } }
		/// <summary>
		/// Klíč události = Id (jde o jednoznačný klíč události)
		/// </summary>
		public string EventKey { get { return null /*this.Id.ToString()*/; } }
		#endregion
        #region Eventy volané z GUI
        /// <summary>
        /// Eventhandler pro událost DoubleClick na elementu.
        /// Je volán jen pro ty události, které mají hodnotu this.ActionOnDoubleClick == ElementActionType.ExecHandler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected void OnDoubleClick(object sender, GraphElementEventArgs args)
        {
            // Nejprve budeme dolovat data:
            Noris.Tools.TraceVisualiser.TraceData.TraceDataSource traceDataSource = args.DataSource as Noris.Tools.TraceVisualiser.TraceData.TraceDataSource;
            if (traceDataSource == null) return;

            Noris.Tools.TraceVisualiser.TraceData.MainInfo traceData = traceDataSource.Main;

            Noris.Tools.TraceVisualiser.TraceData.EventInfo eventInfo = args.DataElement as Noris.Tools.TraceVisualiser.TraceData.EventInfo;
            if (eventInfo == null) return;

            Noris.Tools.TraceVisualiser.Green.SqlServer.EventItem sqlEventItem = eventInfo.EventItem as Noris.Tools.TraceVisualiser.Green.SqlServer.EventItem;
            if (sqlEventItem == null) return;

            // Máme dostatek dat, rozbočíme podle typu eventu:
            switch (sqlEventItem.SqlEventId)
            {
                case SQLTRACE_EVENT_DeadlockGraph:
                    using (LockVisualiser lv = new LockVisualiser())
                    {
                        lv.ShowForm(args);
                    }
                    args.Handled = true;
                    break;
            }
        }
		#endregion
		#region private proměnné: pár jich existovat musí (Int32.TryParse(string, out proměnná)
		#endregion
        #region Tvorba textu Caption a Tooltip
        /// <summary>
        /// Vrátí sadu textů pro Captions. Nadřízená metoda si z nich vybere text vhodný pro aktuální velikost elementu.
        /// </summary>
        /// <returns></returns>
        protected virtual string[] CreateCaptions()
        {
			if (this.ContainSqlCommand)
			{
				string sqlWord = this.FindSqlCommandInTextData(true);
				string sqlComm = this.FindSqlCommandInTextData(false);
                string sqlShr1 = (sqlComm.Length < 25 ? sqlComm : sqlComm.Substring(0, 25) + "...");
                string sqlShr2 = (sqlComm.Length < 75 ? sqlComm : sqlComm.Substring(0, 75) + "...");
                string sqlShr3 = (sqlComm.Length < 150 ? sqlComm : sqlComm.Substring(0, 150) + "...");
				return new string[]
                {
                    sqlWord, 
                    sqlWord + ";  " + this.SqlEventName, 
                    sqlWord + ";  " + this.SqlEventId + ":  " + this.SqlEventName, 
                    sqlWord + ";  " + this.SqlEventId + ":  " + this.SqlEventName + "  " + sqlShr1,
                    sqlWord + ";  " + this.SqlEventId + ":  " + this.SqlEventName + "  " + sqlShr2,
                    sqlWord + ";  " + this.SqlEventId + ":  " + this.SqlEventName + "  " + sqlShr3,
                    sqlWord + ";  " + this.SqlEventId + ":  " + this.SqlEventName + "  " + sqlComm
                };
			}
			else
			{
				return new string[] { this.SqlEventName };
			}
        }
        /// <summary>
        /// Vrátí ToolTip, vždy když je potřeba.
        /// </summary>
        /// <returns></returns>
        protected virtual string CreateToolTipInfo()
        {
            string eol = Environment.NewLine;
            string toolTip = "ID: " + this.SqlEventId + ":  " + this.SqlEventName + eol +
                eol +
                this.Time.StringFullDetail + eol +
                _GetTraceText() + eol +
                "SqlServerInformation: " + this.SqlServerName + eol;

            if (this.SpID > 0)
                toolTip += eol + "SPID: " + this.SpID.ToString();
            if (this.ApplicationNameInt32 != 0)
                toolTip += ";  ApplicationName: " + this.ApplicationName;
            if (this.LoginNameInt32 != 0)
                toolTip += ";  LoginName: " + this.LoginName;

            toolTip += eol + "Duration: " + this.Duration.ToString() +
                       ";  CPU: " + this.CPU.ToString() +
                       ";  Reads: " + this.Reads.ToString() +
                       ";  Writes: " + this.Writes.ToString();

			if (!String.IsNullOrEmpty(this.TextData))
			{
				string text = ((this.ContainSqlCommand) ? this.FindSqlCommandInTextData(false) : this.TextData);
				if (text.Length > 350)
					text = text.Substring(0,346) + "....";

				toolTip += eol + eol + text;
			}

            return toolTip;
        }
		private string _GetTraceText()
        {
            if (this.Trace == null) return "TraceFile: ???";
            return "TraceFile: " + this.Trace.FileDirName + ", Event No: " + this.LineNumber.ToString();
        }
        #endregion
        #region Získání SQL příkazu a parametrů z textu 
        /// <summary>
        /// Najde a vrátí SQL příkaz / první slovo z něj, z this.TextData
        /// </summary>
        /// <returns></returns>
        protected string FindSqlCommandInTextData(bool onlyFirst)
        {
            string text = this.TextData;
            if (String.IsNullOrEmpty(text)) return "";
            string find = "exec sp_executesql ";
            if (text.Length >= find.Length && text.StartsWith(find, StringComparison.InvariantCultureIgnoreCase))
            {
                this.SqlParse(text);
                return FindSqlCommandInTextAfter(text, find, onlyFirst);
            }
            return FindSqlCommandFirstAll(text, onlyFirst);
        }
        /// <summary>
        /// Vrátí první slovo nebo celý text, podle parametru OnlyFirst
        /// </summary>
        /// <param name="text"></param>
        /// <param name="onlyFirst"></param>
        /// <returns></returns>
        private string FindSqlCommandFirstAll(string text, bool onlyFirst)
        {
            if (String.IsNullOrEmpty(text)) return "";
            if (!onlyFirst) return text;
            int at = text.IndexOf(" ");
            if (at > 0) return text.Substring(0, at);
            return text;
        }
        /// <summary>
        /// Vyextrahuje SQL text
        /// </summary>
        /// <param name="text"></param>
        /// <param name="find"></param>
        /// <returns></returns>
        private string FindSqlCommandInTextAfter(string text, string find, bool onlyFirst)
        {
            if (String.IsNullOrEmpty(text)) return "";
            int at = text.IndexOf(find);
            if (at < 0) return text;
            string input = text.Substring(at);
            StringBuilder sb = new StringBuilder();
            char apos = '\'';
            int len = input.Length;
            int last = len - 1;
            int pos = 0;              // 0 = před stringem  1 = ve stringu   2 = za stringem
            for (int i = 0; i < len; i++)
            {
                bool isLast = (i == last);
                char c = input[i];
                switch (pos)
                {
                    case 0:
                        if (c == apos)
                            pos = 1;
                        break;
                    case 1:
                        if (c == apos)
                        {
                            if (isLast)
                                pos = 2;
                            else
                            {
                                char d = input[i + 1];
                                if (d == apos)
                                    i++;
                                else
                                    pos = 2;
                            }
                        }
                        if (pos == 1)
                            sb.Append(c);
                        break;
                }
                if (pos == 2)
                    break;
            }
            return FindSqlCommandFirstAll(sb.ToString(), onlyFirst);
        }
        protected void SqlParse(string text)
        {
            // Noris.Tools.TraceVisualiser.SqlParser.SqlText st = Noris.Tools.TraceVisualiser.SqlParser.SqlText.CreateFromText(text);
        }
        #endregion
        #region IEventPointer Members
        string IEventPointer.ProcessKey { get { return this.ProcessKey; } }
        string IEventPointer.EventKey { get { return this.EventKey; } }
        int IEventPointer.LineNumber { get { return this.LineNumber; } }
        #endregion
        #region IEventItem Members
        string IEventItem.MergeKey { get { return this.MergeKey; } }
        string IEventItem.TargetHomeNode { get { return this.TargetHomeNode; } }
        string[] IEventItem.TargetPaths { get { return this.TargetPaths; } }
        TreePath[] IEventItem.TreePaths { get { return this.TreePaths; } }
        TimeRange IEventItem.Time { get { return this.Time; } }
        void IEventItem.AddData(IEventEdge eventEdge, TraceInfo traceInfo)
        { }
        bool IEventItem.MergeWith(IEventItem nextEventItem) { return this.MergeWith(nextEventItem as EventItem); }
        int IEventItem.ColorIndex { get { return this.ColorIndex; } }
        #endregion
        #region IDataElement Members
        bool IDataElement.ElementVisible { get { return true; } }
        GID IDataElement.GId { get { return GID.Empty; } }                            // Zde není GID uložen, nemohu jej určit, vrátím Empty. On tento objekt de facto není plnohodnotný IDataElement (právě chybějícím GIDem), ale nechce se mi tvořit jiný interface, shodný s IDataElement až na nepřítomnost GIDu...
        TimeRange IDataElement.TimeRange { get { return this.Time; } set { } }
		float IDataElement.ElementHeightRatio { get { return 0f; } }
		bool IDataElement.BeginIsUncertain { get { return false; } }
        bool IDataElement.EndIsUncertain { get { return false; } }
        int IDataElement.SubTimeSort { get { return _GetSubTimeSort(); } }
        bool IDataElement.FullRow { get { return false; } }
		bool IDataElement.HideZeroWidthElementRectangle { get { return this.HideZeroWidthElementRectangle; } }
        float[] IDataElement.Fragments { get { return null; } }
        GraphLayerType IDataElement.ElementLayer { get { return GraphLayerType.ElementItemLayer; } }
        int IDataElement.ElementGroup { get { return this.ElementGroup; } }
        GraphElementShapeType IDataElement.ElementShapeType { get { return this.ElementShapeType; } }
        ElementProperties IDataElement.Properties { get { return this.Properties; } }
        ElementActionType IDataElement.ActionOnDoubleClick { get { return this.ActionOnDoubleClick; } }
        GraphElementHandler IDataElement.DoubleClick { get { return this.OnDoubleClick; } }
		string IDataElement.ColorGroup { get { return this.ColorGroup; } }
        Color IDataElement.BackColor { get { return this.Trace.Main.GetColor(this.ColorGroup, this.Id); } set { } }
        Color IDataElement.FontColor { get { return this.FontColor; } }
        bool IDataElement.CaptionShowAllways { get { return true; } }
        Image IDataElement.ElementImage { get { return this.ElementImage; } }
        ContentAlignment IDataElement.ImagePivotElementPoint { get { return this.ImagePivotElementPoint; } }
		ContentAlignment IDataElement.ImagePivotImagePoint { get { return this.ImagePivotImagePoint; } }
        string[] IDataElement.Captions { get { return CreateCaptions(); } }
        string IDataElement.ToolTipInfo { get { return CreateToolTipInfo(); } }
        string IDataElement.GetToolTipInfo(IDataRow rowData) { return null; }
        string IDataElement.GetCaption(IDataRow rowData, float charCount) { return null; }
        void IDataElement.DrawElement(GraphElementPainterArgs args) { }
        /// <summary>
        /// Vrátí SubTimeSort = číslo trace v čase + číslo řádky
        /// </summary>
        /// <returns></returns>
        private int _GetSubTimeSort()
        {
            // Vrací Int32, který obsahuje bit 31 = 0 (kladné číslo), bity 30 ÷ 25 (6 bitů) = číslo TraceOrder, bity 24 ÷ 0 = číslo LineNumbers[0]:
            return (this.Trace == null ? 0 : (this.Trace.TraceOrder & 0x003F) << 25) | (this.LineNumber & 0x00FFFFFF);
        }
        #endregion
        #region IDisposable Members
        public void Dispose()
        {
            this.StringHeap = null;
        }
        #endregion
        #region IMasterDetails Members
        string IMasterDetails.MasterName
        {
            get
            {
                return this.Time.Begin + "; " + this.SqlEventName;
            }
        }
        Color IMasterDetails.DetailBackColor { get { return this.BackColor; } }
        public IEnumerable<MasterDetailItem> Details { get { return this.CreateDetails(); } }
        /// <summary>
        /// Vytvoří, naplní a vrátí seznam informací Details.
        /// postupně volá metody DetailFillTraceTime(); DetailFill(); DetailFillUserData();
        /// </summary>
        /// <returns></returns>
        protected virtual IEnumerable<MasterDetailItem> CreateDetails()
        {
            List<MasterDetailItem> result = new List<MasterDetailItem>();

            this.DetailFillTraceTime(result);
            this.DetailFillItems(result);
            // this.DetailFillUserData(result);

            return result;
        }
        /// <summary>
        /// Do seznamu detailních údajů vepíše položky ze své evidence.
        /// Třída EventItem vkládá tato data: "Trace, LineNumber"
        /// </summary>
        /// <param name="result"></param>
        protected virtual void DetailFillTraceTime(List<MasterDetailItem> result)
        {
            // Trace, lines, time:
            this.DetailFillTraceInfo("", this.Trace, this.LineNumber, result);
            result.Add(new MasterDetailItem("Time", this.Time.StringFullDetail));
        }
        /// <summary>
        /// Do sady řádků (detaily) vloží data o daném trace.
        /// </summary>
        /// <param name="title">SubTitulek: například "", ".Begin", ".End" atd</param>
        /// <param name="traceInfo">Objekt TraceInfo. Může být null.</param>
        /// <param name="lineNumber">Číslo řádku. Pokud bude null, nebude se vypisovat.</param>
        /// <param name="result">Seznam řádků, kam se detaily vkládají.</param>
        protected void DetailFillTraceInfo(string title, TraceInfo traceInfo, Int32? lineNumber, List<MasterDetailItem> result)
        {
            if (traceInfo == null)
            {
                result.Add(new MasterDetailItem("Trace" + title, "???"));
            }
            else
            {
                result.Add(new MasterDetailItem("Trace" + title, traceInfo.FullFileName));
                if (lineNumber.HasValue)
                    result.Add(new MasterDetailItem("Trace" + title + ".Line", lineNumber.Value.ToString()));
            }
        }
        /// <summary>
        /// Do seznamu detailních údajů vepíše položky ze své evidence.
        /// </summary>
        /// <param name="result"></param>
        protected virtual void DetailFillItems(List<MasterDetailItem> result)
        {
            result.Add(new MasterDetailItem("EventId", this.SqlEventId));
			result.Add(new MasterDetailItem("EventName", this.SqlEventName));
			result.Add(new MasterDetailItem("SpID", this.SpID));
			result.Add(new MasterDetailItem("TextData", this.TextData, new Size(650, 60)));

			if (this.ContainSqlCommand)
				result.Add(new MasterDetailItem("SqlCommand", this.FindSqlCommandInTextData(false), new Size(650, 60)));

			result.Add(new MasterDetailItem("ClientProcessID", this.ClientProcessID));
			result.Add(new MasterDetailItem("ApplicationName", this.ApplicationName));
			result.Add(new MasterDetailItem("LoginName", this.LoginName));
			result.Add(new MasterDetailItem("Duration", this.Duration));
			result.Add(new MasterDetailItem("Reads", this.Reads));
			result.Add(new MasterDetailItem("Writes", this.Writes));
			result.Add(new MasterDetailItem("CPU", this.CPU));

			if (this._Columns != null)
			{
				foreach (EventItemColumn item in this.Columns)
				{
					result.Add(new MasterDetailItem(item.ColumnName, item.ColumnValue));
				}
			}
		}
        #endregion
        #region class EventItemColumn : úložiště pro záznam jakýchkoli dalších dat v eventu, která nejsou uložena do jmenovité property
        /// <summary>
        /// EventItemColumn : úložiště pro záznam jakýchkoli dalších dat v eventu, která nejsou uložena do jmenovité property
        /// </summary>
        internal class EventItemColumn
        {
            internal EventItemColumn(EventItem parent, string columnId, string columnName, string columnValue)
            {
                this.Parent = parent;
                this.ColumnId = columnId;
                this.ColumnName = columnName;
                this.ColumnValue = columnValue;
            }
            internal EventItem Parent { get; private set; }
		    internal string ColumnId { get { return this.Parent.StringHeap.GetString(this.ColumnIdInt32); } private set { this.ColumnIdInt32 = this.Parent.StringHeap.GetInt32(value); } }
		    internal Int32 ColumnIdInt32 { get; private set; }
		    internal string ColumnName { get { return this.Parent.StringHeap.GetString(this.ColumnNameInt32); } private set { this.ColumnNameInt32 = this.Parent.StringHeap.GetInt32(value); } }
		    internal Int32 ColumnNameInt32 { get; private set; }
		    internal string ColumnValue { get { return this.Parent.StringHeap.GetString(this.ColumnValueInt32); } private set { this.ColumnValueInt32 = this.Parent.StringHeap.GetInt32(value); } }
		    internal Int32 ColumnValueInt32 { get; private set; }
        }
        #endregion
        #region ISqlEventData Members
        int ISqlEventData.SqlPId { get { return this.SpID; } }
        string ISqlEventData.Login { get { return this.LoginName; } }
        TimeRange ISqlEventData.Time { get { return this.Time; } }
        string ISqlEventData.SqlComment { get { return "EventName: " + this.SqlEventName; } }
        string ISqlEventData.SqlCommand { get { return this.TextData; } }
        #endregion
    }
    #endregion
    #region enum EventPartType : informace, zda event je hotový, nebo zda je to jen počíteční anebo koncová polovina.
    /// <summary>
    /// EventPartType : informace, zda event je hotový, nebo zda je to jen počíteční anebo koncová polovina.
    /// </summary>
    internal enum EventPartType
    {
        /// <summary>
        /// Žádná část, tento event se má ignorovat
        /// </summary>
        None,
        /// <summary>
        /// Počátek eventu. Musíme dohledat jeho konec (počkat na něj).
        /// </summary>
        Start,
        /// <summary>
        /// Konec eventu. Musíme najít jeho počátek.
        /// </summary>
        End,
        /// <summary>
        /// Kompletní event, může se uložit.
        /// </summary>
        Complete
    }
    #endregion
}
